package com.lcy.lambda.stream;

import com.lcy.lambda.data.Author;
import com.lcy.lambda.data.Book;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.lcy.lambda.data.Author.getAuthors;

/**
 * forEach、count、max&min
 * collect（重点掌握）
 * anyMatch、allMatch、noneMatch、findAny、findFirst
 * reduce
 * <p>
 * 注意事项
 * 1.惰性求值（如果没有终结操作，没有中间操作是不会得到执行的）
 * 2.流是一次性的（一旦一个流对象经过一个终结操作后。这个流就不能再被使用）
 * 3.不会影响原数据（我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的）
 *
 * @author lcy
 * @date 2024/9/23 17:24
 */
public class 终结操作 {


    /**
     * 对流中的元素进行遍历操作，我们通过传入的参数去指定对遍历到的元素进行什么具体操作。
     */
    public void forEach() {
        //输出所有作家的名字
        List<Author> authors = getAuthors();
        authors.stream().distinct().forEach(author -> System.out.println(author.getName()));
    }

    /**
     * 对流中的元素进行统计
     */
    public void count() {
        //统计所有女朋友的个数，去重
        List<Author> authors = getAuthors();
        long count = authors.stream().distinct().count();
        System.out.println("我有" + count + "个女朋友");
    }

    /**
     * 对流中的元素进行统计
     */
    public void count2() {
        //        打印这些女友的所出书籍的数目，注意删除重复元素。
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
    }


    /**
     * 求最大最小值
     */
    public void maxOrMin() {
        //最小的一个女朋友
        List<Author> authors = getAuthors();
        Optional<Integer> min = authors.stream().distinct().map(author -> author.getAge()).min((age1, age2) -> age1 - age2);
        Optional<Integer> max = authors.stream().distinct().map(author -> author.getAge()).max(Comparator.comparingInt(age -> age));

        System.out.println(min.get());
        System.out.println(max.get());
    }

    /**
     * 把当前流转换成一个集合
     */
    public void listCollect() {
        //        获取一个存放所有作者名字的List集合。
        List<Author> authors = getAuthors();
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(nameList);
    }

    /**
     * 把当前流转换成一个集合
     */
    public void setCollect() {
        //        获取一个所有书名的Set集合
        List<Author> authors = getAuthors();
        Set<Book> collect = authors.stream().flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        System.out.println(collect);
    }

    /**
     * 把当前流转换成一个集合
     * 注意：key不能重复，否则会报错。 如果不想报错需要再传入一个合并函数，注意合并函数比较的是value而不是key
     */
    public void mapCollect() {
        //        获取一个Map集合，map的key为作者名，value为List<Book>
        List<Author> authors = getAuthors();
        //会出错，因为有两个管媛媛
//        Map<String, List<Book>> map = authors.stream().collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));

        //如果提供一个合并函数告诉：
        Map<String, List<Book>> map = authors.stream().collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks(), new BinaryOperator<List<Book>>() {
            @Override
            public List<Book> apply(List<Book> existing, List<Book> replacement) {
                //如果作者冲突了，那么返回书多的那一个
                return existing.size() > replacement.size() ? existing : replacement;
            }
        }));
        System.out.println(map);

        //常用：如果是把对象本身最为value的写法，如果key重复，用最新的或者丢弃
        Map<String, Author> map2 = authors.stream().collect(Collectors.toMap(
                author -> author.getName(),
                author -> author,     //Function.identity()也是一种写法
                (existing, replacement) -> {
            //如果作者冲突了，那么返回书多的那一个
            return replacement;
        }));
        System.out.println(map);
    }

    /**
     * 把当前流按某个字段进行分组
     */
    public void groupCollect() {

        List<Author> authors = getAuthors();
        Map<String, List<Author>> collect = authors.stream().collect(Collectors.groupingBy(author -> author.getName()));
        collect.entrySet().forEach(System.out::println);
    }

    /**
     *  其他一些功能比如：
     *      找出最大最小值：max&min
     *      anyMatch：可以用来判断是否有任意符合匹配条件的元素，结果为boolean类型
     *      allMatch：可以用来判断是否都符合匹配条件，结果为boolean类型。如果都符合结果为true，否则结果为false。、
     *      noneMatch：可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true，否则结果为false。
     *      findAny： 获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。
     *      findFirst：获取流中的第一个元素。
     *
     */
    public void otherFunction() {

    }

    /**
     *  并归：对流中的数据按照你指定的计算方式计算出一个结果。（缩减操作）
     *  reduce的作用是把stream中的元素给组合起来，我们可以传入一个初始值，它会按照我们的计算方式依次拿流中的元素和初始化值进行计算，计算结果再和后面的元素计算。
     *   reduce两个参数的重载形式内部的计算方式如下：
     *
     *   T result = identity;
     *   for (T element : this stream)
     *    result = accumulator.apply(result, element)
     *   return result;
     *   其中identity就是我们可以通过方法参数传入的初始值，accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。
     */
    public void reduce() {
        //        使用reduce求所有作者年龄的和
        List<Author> authors = getAuthors();
        int sum = 10;
        sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(sum, (result, element) -> result + element);
        System.out.println(sum);


        //        使用reduce求所有作者中年龄的最大值
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);

        System.out.println(max);
    }

    public static void main(String[] args) {
        终结操作 终结操作 = new 终结操作();
//        终结操作.forEach();
//        终结操作.count();
//        终结操作.count2();
//        终结操作.maxOrMin();
//        终结操作.maxOrMin();
//        终结操作.listCollect();
//        终结操作.setCollect();
//        终结操作.mapCollect();
        终结操作.groupCollect();
//        终结操作.reduce();
    }
}
